{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>第9章 性能优化技术*</center>\n",
    "\n",
    "<br>\n",
    "\n",
    "- [9.1 Python程序性能分析](#9.1-Python程序性能分析)\n",
    "  - [9.1.1 `time`与`timeit`](#9.1.1-time与timeit)\n",
    "  - [9.1.2 `profile`](#9.1.2-profile)\n",
    "- [9.2 即时编译技术](#9.2-即时编译技术)\n",
    "  - [9.2.1 即时编译的概念](#9.2.1-即时编译的概念)\n",
    "  - [9.2.2 PyPy](#9.2.2-PyPy)\n",
    "  - [9.2.3 Numba](#9.2.3-Numba)\n",
    "- [9.3 混合编程概念及环境搭建](#9.3-混合编程概念及环境搭建)\n",
    "- [9.4 利用 ctypes 实现混合编程](#9.4-利用-ctypes-实现混合编程)\n",
    "  - [9.4.1 C 函数库的调用](#9.4.1-C-函数库的调用)\n",
    "  - [9.4.2 C++ 类的包装](#9.4.2-C++-类的包装)\n",
    "- [9.5 利用 C API 构建 Python 扩展](#9.5-利用-C-API-构建-Python-扩展)\n",
    "  - [9.5.1 构建 Python 扩展的步骤](#9.5.1-构建-Python-扩展的步骤)\n",
    "  - [9.5.2 扩展函数](#9.5.2-扩展函数)\n",
    "  - [9.5.3 模块配置与初始化](#9.5.3-模块配置与初始化)\n",
    "  - [9.5.4 扩展的构建与安装](#9.5.4-扩展的构建与安装)\n",
    "  - [9.5.5 示例](#9.5.5-示例)\n",
    "- [9.6 项目打包与发布](#9.6-项目打包与发布)\n",
    "  - [9.6.1 打包与发布的流程](#9.6.1-打包与发布的流程)\n",
    "  - [9.6.2 项目打包与发布示例](#9.6.2-项目打包与发布示例)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 9.1 Python程序性能分析\n",
    " \n",
    "- 程序性能分析的作用\n",
    "  - 确定程序运行效率低的原因\n",
    "  - 寻找代码的运行瓶颈\n",
    "  - 对症下药选用最恰当的性能优化技术"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.1.1 `time`与`timeit`\n",
    "\n",
    "#### `time`\n",
    "\n",
    "使用`time`模块获取程序的运行时间常用如下几种方式：\n",
    "\n",
    "- `time.time`：获取系统当前时间的时间戳；\n",
    "- `time.perf_counter`：获取当前程序的高精度的CPU级运行时间；\n",
    "- `time.process_time`：获取当前程序的有效进程时间"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "perf_counter: 1.0736476539996147\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "def fun():\n",
    "    sum_value = 0\n",
    "    for i in range(1000000):\n",
    "        sum_value += i\n",
    "\n",
    "start = time.perf_counter()\n",
    "fun()\n",
    "time.sleep(1)\n",
    "end = time.perf_counter()\n",
    "print('perf_counter:', end - start)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "process_time: 0.07273000000000018\n"
     ]
    }
   ],
   "source": [
    "start = time.process_time()\n",
    "fun()\n",
    "time.sleep(1)\n",
    "end = time.process_time()\n",
    "print('process_time:', end - start)  # process_time中不包含time.sleep(1)造成的程序休眠时间"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### ` timeit`\n",
    "\n",
    "- `timeit`常用于测试较小的代码片段的执行时间\n",
    "- 运行方式\n",
    "  - 命令行方式\n",
    "  - Python接口"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "\n",
    "- 命令行方式\n",
    "\n",
    "```bash\n",
    "$ python -m timeit '\"-\".join(str(n) for n in range(100))'\n",
    "10000 loops, best of 5: 23.5 usec per loop\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "- 命令行常用的参数如下表所示。\n",
    "\n",
    "| 参数        | 缩写 | 功能                                             |\n",
    "| :----------- | :---- | :------------------------------------------------ |\n",
    "| `--number`  | `-n` | 代码片段执行次数                                 |\n",
    "| `--repeat`  | `-r` | 重复次数                                         |\n",
    "| `--setup`   | `-s` | 准备运行环境，如`import`语句等                   |\n",
    "| `--process` | `-p` | 测量进程时间                                     |\n",
    "| `--unit`    | `-u` | 时间单位，可以取值` nsec`、`usec`、`msec`或`sec` |\n",
    "\n",
    "```bash\n",
    "$ python -m timeit -n 2000 -r 10 '\"-\".join(str(n) for n in range(100))'\n",
    "2000 loops, best of 10: 23.5 usec per loop\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- Python接口\n",
    "  - `timeit.timit`函数\n",
    "  - `timeit.repeat`函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.2476290219992734"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import timeit\n",
    "timeit.timeit('\"-\".join(str(n) for n in range(100))', number=10000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0.2479564710001796,\n",
       " 0.2286210049996953,\n",
       " 0.2277683940001225,\n",
       " 0.24715779699999985,\n",
       " 0.23120317899974907]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.repeat('\"-\".join(str(n) for n in range(100))', number=10000, repeat=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 在脚本代码中测试函数执行时间"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.5808773019998625\n",
      "[0.5691154360001747, 0.5654969030001666, 0.5676186800001233]\n"
     ]
    }
   ],
   "source": [
    "import timeit\n",
    "\n",
    "def bubble_sort(lst):\n",
    "    \"\"\"冒泡排序\"\"\"\n",
    "    for i in range(len(lst)-1, 0, -1):\n",
    "        for j in range(0, i):\n",
    "            if lst[j] > lst[j + 1]:\n",
    "                lst[j], lst[j+1] = lst[j+1], lst[j]\n",
    "    return lst\n",
    "\n",
    "t1 = timeit.timeit('bubble_sort(lst)',\n",
    "                  setup='''from __main__ import bubble_sort; import random; lst = list(range(1000)); random.shuffle(lst)''',\n",
    "                  number=10)\n",
    "\n",
    "t2 = timeit.repeat('bubble_sort(lst)',\n",
    "                  setup='''from __main__ import bubble_sort; import random; lst = list(range(1000)); random.shuffle(lst)''',\n",
    "                  number=10,\n",
    "                  repeat=3)\n",
    "print(t1)\n",
    "print(t2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.1.2 `profile`\n",
    "\n",
    "- profile是Python内置一组用于收集和分析Python 程序执行过程的工具\n",
    "  - 能够程序运行过程中每个函数调用时间的详细统计数据\n",
    "- profile的两种实现\n",
    "  - `profile`\n",
    "    - Python内置标准库中的模块\n",
    "    - 会显著增加程序的运行开销，适用于需要对分析功能进行扩展的场景\n",
    "  - `cProfile`\n",
    "    - `profile`的C扩展插件\n",
    "    - 自身运行开销较小，适合于分析长时间运行的程序"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### `cProfile`\n",
    "\n",
    "- `test_profile.py`\n",
    "\n",
    "```python\n",
    "def fib(n):\n",
    "    if n == 0:\n",
    "        return 0\n",
    "    elif n == 1:\n",
    "        return 1\n",
    "    else:\n",
    "        return fib(n - 1) + fib(n - 2)\n",
    "\n",
    "def fib_list(n):\n",
    "    seq = []\n",
    "    if n > 0:\n",
    "        seq.extend(fib_list(n - 1))\n",
    "    seq.append(fib(n))\n",
    "    return seq\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    fib_list(30)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "```bash\n",
    "$ python -m cProfile test_profile.py\n",
    "```\n",
    "```\n",
    "         7049218 function calls (96 primitive calls) in 2.015 seconds\n",
    "\n",
    "   Ordered by: standard name\n",
    "\n",
    "   ncalls  tottime  percall  cumtime  percall filename:lineno(function)\n",
    "        1    0.000    0.000    2.015    2.015 test_profile.py:1(<module>)\n",
    "7049123/31    2.014    0.000    2.014    0.065 test_profile.py:1(fib)\n",
    "     31/1    0.000    0.000    2.015    2.015 test_profile.py:10(fib_list)\n",
    "        1    0.000    0.000    2.015    2.015 {built-in method builtins.exec}\n",
    "       31    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}\n",
    "        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}\n",
    "       30    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 共检测了7049218个函数调用，其中96个是原始调用（非递归调用）。函数`fib`的递归调用次数为`7049123`，原始调用次数为`31`，耗费总时间是`2.014`\n",
    "\n",
    "- 分析结果中各列数值的含义如下表所示。\n",
    "\n",
    "| 列名                      | 含义                                                         |\n",
    "| ------------------------- | ------------------------------------------------------------ |\n",
    "| ncalls                    | 调用次数（若存在递归调用则分别显示递归调用次数和原始调用次数） |\n",
    "| tottime                   | 函数调用总时间（不包括调用子函数的时间）                     |\n",
    "| percall                   | 平均调用时间（ tottime 除以 ncalls ）                        |\n",
    "| cumtime                   | 函数及其所有子函数消耗的累积时间（对于递归函数来说是准确的） |\n",
    "| percall                   | 函数运行一次的平均时间（cumtime 除以原始调用次数）           |\n",
    "| filename:lineno(function) | 函数所在的文件、行数及函数名                                 |\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "         7049218 function calls (96 primitive calls) in 1.998 seconds\n",
      "\n",
      "   Ordered by: standard name\n",
      "\n",
      "   ncalls  tottime  percall  cumtime  percall filename:lineno(function)\n",
      "7049123/31    1.997    0.000    1.997    0.064 <ipython-input-6-3823c2297ba6>:1(fib)\n",
      "     31/1    0.000    0.000    1.998    1.998 <ipython-input-6-3823c2297ba6>:9(fib_list)\n",
      "        1    0.000    0.000    1.998    1.998 <string>:1(<module>)\n",
      "        1    0.000    0.000    1.998    1.998 {built-in method builtins.exec}\n",
      "       31    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}\n",
      "        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}\n",
      "       30    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fib(n):\n",
    "    if n == 0:\n",
    "        return 0\n",
    "    elif n == 1:\n",
    "        return 1\n",
    "    else:\n",
    "        return fib(n - 1) + fib(n - 2)\n",
    "\n",
    "def fib_list(n):\n",
    "    seq = []\n",
    "    if n > 0:\n",
    "        seq.extend(fib_list(n - 1))\n",
    "    seq.append(fib(n))\n",
    "    return seq\n",
    "\n",
    "import cProfile\n",
    "cProfile.run('fib_list(30)')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "         7049260 function calls (138 primitive calls) in 2.040 seconds\n",
      "\n",
      "   Ordered by: internal time\n",
      "\n",
      "   ncalls  tottime  percall  cumtime  percall filename:lineno(function)\n",
      "7049123/31    2.040    0.000    2.040    0.066 <ipython-input-6-3823c2297ba6>:1(fib)\n",
      "     31/1    0.000    0.000    2.040    2.040 <ipython-input-6-3823c2297ba6>:9(fib_list)\n",
      "        2    0.000    0.000    0.000    0.000 {built-in method builtins.compile}\n",
      "        2    0.000    0.000    2.040    1.020 interactiveshell.py:3293(run_code)\n",
      "       30    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}\n",
      "       31    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}\n",
      "        2    0.000    0.000    0.000    0.000 codeop.py:135(__call__)\n",
      "        2    0.000    0.000    0.000    0.000 contextlib.py:82(__init__)\n",
      "        4    0.000    0.000    0.000    0.000 compilerop.py:138(extra_flags)\n",
      "        1    0.000    0.000    0.000    0.000 <ipython-input-7-9649c2a4d5ce>:5(<module>)\n",
      "        2    0.000    0.000    0.000    0.000 hooks.py:103(__call__)\n",
      "        2    0.000    0.000    0.000    0.000 contextlib.py:117(__exit__)\n",
      "        2    0.000    0.000    0.000    0.000 contextlib.py:108(__enter__)\n",
      "        4    0.000    0.000    0.000    0.000 {built-in method builtins.getattr}\n",
      "        2    0.000    0.000    2.040    1.020 {built-in method builtins.exec}\n",
      "        2    0.000    0.000    0.000    0.000 traitlets.py:545(__get__)\n",
      "        2    0.000    0.000    0.000    0.000 contextlib.py:238(helper)\n",
      "        2    0.000    0.000    0.000    0.000 traitlets.py:526(get)\n",
      "        1    0.000    0.000    2.040    2.040 <ipython-input-7-9649c2a4d5ce>:4(<module>)\n",
      "        2    0.000    0.000    0.000    0.000 interactiveshell.py:3231(compare)\n",
      "        4    0.000    0.000    0.000    0.000 {built-in method builtins.next}\n",
      "        2    0.000    0.000    0.000    0.000 ipstruct.py:125(__getattr__)\n",
      "        2    0.000    0.000    0.000    0.000 interactiveshell.py:1276(user_global_ns)\n",
      "        2    0.000    0.000    0.000    0.000 hooks.py:168(pre_run_code_hook)\n",
      "        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import cProfile\n",
    "p = cProfile.Profile()\n",
    "p.enable()\n",
    "fib_list(30)\n",
    "p.disable()\n",
    "p.print_stats(sort='tottime')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### `pstats`\n",
    "\n",
    "- `cProfile`或`profile`的分析结果可以保存为二进制文件，`pstats`用于对文件中的分析结果进行进一步的统计分析\n",
    "\n",
    "- 将分析结果保存为文件有两种方式：\n",
    "  - 在命令行中：`python -m cProfile -o result.out test_profile.py)`\n",
    "  - 在代码中：`cProfile.run(‘fib_list(30)'), filename=”result.out”)`\n",
    "\n",
    "- `pstats`模块中常用的函数有：\n",
    "  - `strip_dirs`：去掉无关的路径信息\n",
    "  - `sort_stats`：对分析结果进行排序\n",
    "  - `print_stats`：输出分析结果，可以指定输出行数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- `pstats`模块中常用的函数有：\n",
    "  - `strip_dirs`：去掉无关的路径信息\n",
    "  - `sort_stats`：对分析结果进行排序\n",
    "  - `print_stats`：输出分析结果，可以指定输出行数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": true,
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "ename": "FileNotFoundError",
     "evalue": "[Errno 2] No such file or directory: 'result.out'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-8-85ff513ed0d6>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpstats\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpstats\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mStats\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'result.out'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstrip_dirs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msort_stats\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'name'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_stats\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-'\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msort_stats\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'cumulative'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_stats\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/lesson/lib/python3.8/pstats.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, stream, *args)\u001b[0m\n\u001b[1;32m     94\u001b[0m             \u001b[0marg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     95\u001b[0m             \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 96\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     97\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     98\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/lesson/lib/python3.8/pstats.py\u001b[0m in \u001b[0;36minit\u001b[0;34m(self, arg)\u001b[0m\n\u001b[1;32m    108\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstats\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    109\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msort_arg_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 110\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload_stats\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    111\u001b[0m         \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    112\u001b[0m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_top_level_stats\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/lesson/lib/python3.8/pstats.py\u001b[0m in \u001b[0;36mload_stats\u001b[0;34m(self, arg)\u001b[0m\n\u001b[1;32m    121\u001b[0m             \u001b[0;32mreturn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    122\u001b[0m         \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 123\u001b[0;31m             \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'rb'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    124\u001b[0m                 \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstats\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmarshal\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    125\u001b[0m             \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'result.out'"
     ]
    }
   ],
   "source": [
    "import pstats\n",
    "p = pstats.Stats('result.out')\n",
    "p.strip_dirs().sort_stats('name').print_stats()\n",
    "print('-'*100)\n",
    "p.sort_stats('cumulative').print_stats()\n",
    "print('-'*100)\n",
    "p.sort_stats('time').print_stats()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 9.2 即时编译技术\n",
    "\n",
    "### 9.2.1 即时编译的概念\n",
    "\n",
    "- 计算机程序常见运行方式\n",
    "  - 编译执行\n",
    "    - 优点：编译一次可多次执行，运行速度快\n",
    "    - 缺点：难以支持许多动态特性\n",
    "  - 解释执行\n",
    "    - 优点：针对每条语句进行解释，容易实现语言的动态特性\n",
    "    - 缺点：每次执行语句都需要重新编译，运行速度慢\n",
    "- 即时（Just In Time, JIT）编译\n",
    "  - 兼具编译执行和解释执行特点\n",
    "  - 需重复执行的代码被编译、优化并缓存供后续使用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 即时编译的过程\n",
    "\n",
    "<center>\n",
    "    <img src=\"./figures/fig9-1.png\" width=\"80%\"/>\n",
    "    </center>\n",
    "\n",
    "- 基于JIT的Python优化方案\n",
    "  - PyPy\n",
    "  - Numba\n",
    "  - Jyson\n",
    "  - pyston"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.2.2 PyPy\n",
    "\n",
    "- PyPy是使用RPython实现的Python解释器\n",
    "  - RPython是CPython的子集\n",
    "- 安装\n",
    "  - https://www.pypy.org/download.html\n",
    "  - Windows: https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.1-win32.zip\n",
    "  - MacOS: `brew install pypy3`\n",
    "- 特点\n",
    "  - PyPy能够对CPython的部分版本提供较好的支持\n",
    "  - 对第三方工具包的支持比较差\n",
    "    - 有独立的包管理器pip_pypy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.5926239299997178\n"
     ]
    }
   ],
   "source": [
    "# pypy_test.py\n",
    "import timeit\n",
    "def bubble_sort(lst):\n",
    "    \"\"\"冒泡排序\"\"\"\n",
    "    for i in range(len(lst)-1, 0, -1):\n",
    "        for j in range(0, i):\n",
    "            if lst[j] > lst[j + 1]:\n",
    "                lst[j], lst[j+1] = lst[j+1], lst[j]\n",
    "    return lst\n",
    "\n",
    "t = timeit.timeit('bubble_sort(lst)',\n",
    "                  setup='''from __main__ import bubble_sort; \\\n",
    "                  import random; lst = list(range(1000)); random.shuffle(lst)''',\n",
    "                  number=10)\n",
    "print(t)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```bash\n",
    "$ python pypy_test.py \n",
    "0.571357171\n",
    "$\n",
    "$ pypy3 pypy_test.py \n",
    "0.022235398006159812\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.2.3 Numba\n",
    "\n",
    "- Numba\n",
    "  - 是一种以Python第三方工具包的形式实现的即时编译器\n",
    "  - 适用于包含了`Numpy`数组、函数和循环的代码\n",
    "  - `pip install numba`\n",
    "- 使用\n",
    "  - 利用装饰器修饰函数或类\n",
    "    - 在程序执行的过程中相应的代码片段不再使用Python解释器执行，而是使用`Numba`的即时编译器执行\n",
    "  - 适用场景\n",
    "    - 对于数学运算类型的任务、包含了较多循环语句的代码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 基本用法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "import timeit\n",
    "from numba import jit\n",
    "\n",
    "def factorial(n):\n",
    "    '''阶乘'''\n",
    "    fac = 1\n",
    "    for i in range(1, n+1):\n",
    "        fac = fac*i\n",
    "    return fac\n",
    "\n",
    "@jit\n",
    "def factorial_jit(n):\n",
    "    fac = 1\n",
    "    for i in range(1, n+1):\n",
    "        fac = fac*i\n",
    "    return fac"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.0420013419998213"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit('factorial(10000)', setup='from __main__ import factorial', number=100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7.113244510999721"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit('factorial_jit(10000)', setup='from __main__ import factorial_jit', number=100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### Eager编译\n",
    "\n",
    "- 在`jit`装饰器中指定被修饰函数的签名\n",
    "  - 即时编译器不用再推测参数的数据类型，因而在脚本被导入或运行的时候就可以对函数进行编译"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "from numba import jit, int32\n",
    "\n",
    "@jit(int32(int32, int32))\n",
    "def f(x, y):\n",
    "    return x + y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数的实参类型与签名不一致时可能导致意外的运算结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f(2**31, 2**31 + 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- `numba`中常用的数据类型\n",
    "\n",
    "| 类型                                                         | 含义                                     |\n",
    "| :------------------------------------------------------------ | :---------------------------------------- |\n",
    "| `void`                                                       | 无返回值（或返回`None`）的函数的返回类型 |\n",
    "| `intc`, `uintc`                                              | 相当于 C语言中的 `int`和`unsigned int`   |\n",
    "| `int8`, `uint8`, `int16`, `uint16`, `int32`, `uint32`, `int64`, `uint64` | 相应宽度的有符号或无符号整数             |\n",
    "| `float32`,  `float64`                                        | 单精度和双精度浮点数                     |\n",
    "| `complex64`, `complex128`                                    | 单精度和双精度复数                       |\n",
    "| `int32[:]`, `float[:32]`                                     | 数组，可以是其他任意类型的数组           |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### nopython 模式\n",
    "\n",
    "- Numba即时编译器有两种编译模式\n",
    "  - nopython 模式\n",
    "    - 编译器对函数进行编译，在运行过程不需要Python解释器的参与\n",
    "  - 对象模式\n",
    "    - 编译能够识别的部分，其余的部分还交给Python解释器执行\n",
    "- 默认情况下\n",
    "  - 当nopython模式编译失败时，`numba`可以切换至对象模式\n",
    "- 在`jit`装饰器中指定`nopython=True`\n",
    "  - 可强制使用nopython模式，如果编译失败不会切换至对象模式，而是会抛出错误\n",
    "  - `@jit(nopython=True)`相当于`@njit`装饰器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "@jit(nopython=True)\n",
    "def f(x, y):\n",
    "    return x + y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 缓存编译结果\n",
    "\n",
    "- `jit`装饰器中指定参数`cache=True`\n",
    "  - `numba`会将函数编译的结果保存至文件缓存，再次执行时不必重新编译"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "@jit(cache=True)\n",
    "def f(x, y):\n",
    "    return x + y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 9.3 混合编程概念及环境搭建\n",
    "\n",
    "#### 混合编程\n",
    "\n",
    "- 混合编程\n",
    "  - 将计算密集型的任务利用其他语言实现然后交由 Python 调用，从而实现性能的提升\n",
    "  - Python 与常见的计算机编程语言如 Java、C#，甚至 R 和 Matlab 等 都能够实现混合编程\n",
    "- 混合编程方法\n",
    "  - 在 Python 中调用 C/C++ 的动态库文件\n",
    "    - 利用 Python 标准库中的ctypes实现\n",
    "  - 利用 C/C++ 编写 Python 的扩展，使 C/C++ 库能够像普通 Python 模块一样 使用\n",
    "    - 可以使用 Python 内置的 C 语言 API 实现\n",
    "    - 也可以使用第三方工具如 Cython、Boost.Python、SWIG、pybind11 等来实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 环境搭建\n",
    "\n",
    "- Windows 系统（https://wiki.python.org/moin/WindowsCompilers）\n",
    "  - MingW-w64工具链\n",
    "    - 新建 Anaconda 虚拟环境并激活\n",
    "    - 安装MingW-w64工具链：`conda install libpython m2w64-toolchain -c msys2`\n",
    "    - 配置默认编译器：\n",
    "      - 在虚拟环境所在路径中找到Lib\\distutils文件夹，在其中创建 distutils.cfg 文件并写入如下内容\n",
    "      ```\n",
    "      [build]\n",
    "      compiler=mingw32 \n",
    "      [build_ext] \n",
    "      compiler=mingw32\n",
    "      ```\n",
    "- Linux\n",
    "  - Debian/Ubuntu：`apt-get install build-essential`\n",
    "  - Redhat/CentOS：`yum groupinstall \"development tools\"`\n",
    "- macOS\n",
    "  - 安装Xcode命令行工具即可：`xcode-select --install`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 9.4 利用 ctypes 实现混合编程\n",
    "\n",
    "- ctypes\n",
    "  - Python 标准库中用于调用 C 动态链接库函数的功能模块\n",
    "  - 实现混合编程的一种基础的方法，适用于不太复杂的混合编程应用场景\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.4.1 C 函数库的调用\n",
    "\n",
    "#### 一般 C 函数的调用\n",
    "\n",
    "- 将如下代码保存为文件add.c\n",
    "\n",
    "```C\n",
    "// 文件add.c\n",
    "double add(double x, double y) {\n",
    "    return x + y;\n",
    "}\n",
    "```\n",
    "\n",
    "- 将add.c编译为动态链接库文件\n",
    "  - `gcc -o libadd.so -shared -fPIC add.c`\n",
    "    - `- o`选项用于指定输出的动态链接库文件名\n",
    "    - `- shared`选项用于指定将源代码编译为动态链接库\n",
    "    - `- fPIC`选项用于指定将动态链接库编译为位置无关的代码\n",
    "\n",
    "- 在相同路径中的Python代码中调用\n",
    "\n",
    "```python\n",
    "import ctypes\n",
    "lib = ctypes.cdll.LoadLibrary('./libadd.so')\n",
    "add = lib.add\n",
    "add.argtypes = (ctypes.c_double, ctypes.c_double)  # 参数的数据类型\n",
    "add.restype = ctypes.c_double                      # 返回值的数据类型\n",
    "print(add(1.0, 2))\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- `ctypes` 数据类型\n",
    "\n",
    "| ctypes 类型    | C 类型        | Python 类型       |\n",
    "| :-------------- | :------------- | :-------------- |\n",
    "| `c_bool`       | `_Bool`       | `bool`            |\n",
    "| `c_char`       | `char`        | 长为1的`bytes`    |\n",
    "| `c_wchar`      | `wchar_t`     | 长为1的`str`      |\n",
    "| `c_byte`       | `char`        | `int`             |\n",
    "| `c_short`      | `short`       | `int`             |\n",
    "| `c_int`        | `int`         | `int`             |\n",
    "| `c_long`       | `long`        | `int`             |\n",
    "| `c_longlong`   | `long long`   | `int`             |\n",
    "| `c_float`      | `float`       | `float`           |\n",
    "| `c_double`     | `double`      | `float`           |\n",
    "| `c_longdouble` | `long double` | `float`           |\n",
    "| `c_char_p`     | `char *`      | `bytes` 或 `None` |\n",
    "| `c_wchar_p`    | `wchar_t *`   | `str` 或 `None`   |\n",
    "| `c_void_p`     | `void *  `    | `int` 或 `None`   |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 指针作为参数\n",
    "\n",
    "- C语言文件\n",
    "\n",
    "```C\n",
    "// divide.c\n",
    "int divide(int a, int b, int *remainder) {\n",
    "    int quot = a / b;\n",
    "    *remainder = a % b;\n",
    "    return quot;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 编译\n",
    "\n",
    "```\n",
    "gcc -o libdivide.so -shared -fPIC divide.c\n",
    "```\n",
    "\n",
    "- 调用\n",
    "\n",
    "```python\n",
    "import ctypes\n",
    "_lib = ctypes.cdll.LoadLibrary('libdivide.so')\n",
    "_divide = _lib.divide\n",
    "_divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))\n",
    "_divide.restype = ctypes.c_int\n",
    "\n",
    "def divide(x, y):\n",
    "    rem = ctypes.c_int()\n",
    "    quot = _divide(x, y, rem)\n",
    "    return quot, rem.value\n",
    "```\n",
    "\n",
    "```\n",
    ">>> divide(42, 8)\n",
    "(5, 2)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 数组作为参数\n",
    "\n",
    "- C语言文件\n",
    "\n",
    "```C\n",
    "// avg.c\n",
    "double avg(double *a, int n) {\n",
    "    int i;\n",
    "    double total = 0.0;\n",
    "    for (i = 0; i < n; i++) {\n",
    "        total += a[i];\n",
    "    }\n",
    "    return total / n;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 编译\n",
    "\n",
    "```\n",
    "gcc -o libavg.so -shared -fPIC avg.c\n",
    "```\n",
    "\n",
    "- 调用\n",
    "\n",
    "```python\n",
    "import ctypes\n",
    "_lib = ctypes.cdll.LoadLibrary('libavg.so')\n",
    "\n",
    "class DoubleArray:\n",
    "    def from_param(self, param):\n",
    "        return ((ctypes.c_double)*len(param))(*param)\n",
    "\n",
    "array = DoubleArray()\n",
    "_avg = _lib.avg\n",
    "_avg.argtypes = (array, ctypes.c_int)\n",
    "_avg.restype = ctypes.c_double\n",
    "\n",
    "def avg(values):\n",
    "    return _avg(values, len(values))\n",
    "```\n",
    "\n",
    "```\n",
    ">>> avg([1, 2, 3, 4, 5, 6])\n",
    "3.5\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 结构体作为参数\n",
    "\n",
    "- C语言文件\n",
    "\n",
    "```C\n",
    "// dist.c\n",
    "\n",
    "#include <math.h>\n",
    "\n",
    "typedef struct Point {\n",
    "    double x,y;\n",
    "} Point;\n",
    "\n",
    "double distance(Point *p1, Point *p2) {\n",
    "    return hypot(p1->x - p2->x, p1->y - p2->y);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 编译\n",
    "\n",
    "```\n",
    "gcc -o libdist.so -shared -fPIC dist.c\n",
    "```\n",
    "\n",
    "- 调用\n",
    "\n",
    "```python\n",
    "import ctypes\n",
    "_lib = ctypes.cdll.LoadLibrary('libdist.so')\n",
    "\n",
    "class Point(ctypes.Structure):\n",
    "    _fields_ = [('x', ctypes.c_double),\n",
    "                ('y', ctypes.c_double)]\n",
    "\n",
    "distance = _lib.distance\n",
    "distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))\n",
    "distance.restype = ctypes.c_double\n",
    "```\n",
    "\n",
    "```\n",
    ">>> p1 = Point(1, 2)\n",
    ">>> p2 = Point(4, 5)\n",
    ">>> distance(p1, p2)\n",
    "4.242640687119285\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.4.2 C++ 类的包装\n",
    "\n",
    "- `ctypes`不支持将C++代码，因此需要将C++类转换为C函数来调用\n",
    "  - 如果要以类的方式使用，需要手动将动态链接库中暴露出的C函数映射为Python的类\n",
    "\n",
    "- C++文件\n",
    "\n",
    "```cpp\n",
    "// rectangle.cpp\n",
    "\n",
    "class Rectangle\n",
    "{\n",
    "public:\n",
    "    Rectangle(float, float);\n",
    "    double area();\n",
    "    double perimeter();\n",
    "\n",
    "private:\n",
    "    float width_;\n",
    "    float height_;\n",
    "};\n",
    "\n",
    "Rectangle::Rectangle(float width, float height)\n",
    "{\n",
    "    width_ = width;\n",
    "    height_ = height;\n",
    "}\n",
    "\n",
    "double Rectangle::area()\n",
    "{\n",
    "    return width_ * height_;\n",
    "}\n",
    "\n",
    "double Rectangle::perimeter()\n",
    "{\n",
    "    return 2 * width_ + 2 * height_;\n",
    "}\n",
    "\n",
    "// 以C的方式编译如下函数\n",
    "extern \"C\"\n",
    "{\n",
    "    Rectangle *Rectangle_new(double width, double height)\n",
    "    {\n",
    "        return new Rectangle(width, height);\n",
    "    }\n",
    "    double area(Rectangle *rect)\n",
    "    {\n",
    "        return rect->area();\n",
    "    }\n",
    "    double perimeter(Rectangle *rect)\n",
    "    {\n",
    "        return rect->perimeter();\n",
    "    }\n",
    "}\n",
    "```\n",
    "\n",
    "- 编译\n",
    "\n",
    "```\n",
    "g++ -o librectangle.so -shared -fPIC rectangle.cpp\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 映射Python类\n",
    "\n",
    "```python\n",
    "import ctypes\n",
    "\n",
    "_lib = ctypes.cdll.LoadLibrary('librectangle.so')\n",
    "\n",
    "class Rectangle:\n",
    "    def __init__(self, width, height):\n",
    "        self._methods = dict()\n",
    "        self._methods['area'] = _lib.area\n",
    "        self._methods['perimeter'] = _lib.perimeter\n",
    "\n",
    "        _lib.Rectangle_new.argtypes = (ctypes.c_double, ctypes.c_double)\n",
    "        _lib.Rectangle_new.restype = ctypes.c_void_p\n",
    "\n",
    "        _lib.area.argtypes = (ctypes.c_void_p,)\n",
    "        _lib.area.restype = ctypes.c_double\n",
    "\n",
    "        _lib.perimeter.argtypes = (ctypes.c_void_p,)\n",
    "        _lib.perimeter.restype = ctypes.c_double\n",
    "\n",
    "        self.obj = _lib.Rectangle_new(width, height)\n",
    "        self._m_name = None\n",
    "\n",
    "    def __getattr__(self, attr):\n",
    "        self._m_name = attr\n",
    "        return self.__call_method\n",
    "\n",
    "    def __call_method(self, *args):\n",
    "        return self._methods[self._m_name](self.obj, *args)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 调用\n",
    "\n",
    "```\n",
    ">>> rect = Rectangle(3, 5)\n",
    ">>> rect.area()\n",
    "15.0\n",
    ">>> rect.perimeter()\n",
    "16.0\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 9.5 利用 C API 构建 Python 扩展\n",
    "\n",
    "\n",
    "### 9.5.1 构建 Python 扩展的步骤\n",
    "\n",
    "- 核心\n",
    "  - 将Python数据类型传入C环境，在C环境中调用相应的C功能函数，并将结果转换为Python类型返回Python环境\n",
    "  - 将C函数以Python能够识别的形式暴露出来\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 步骤\n",
    "  - 编写并编译C函数库\n",
    "    - `gcc`\n",
    "  - 编写扩展函数\n",
    "      - `static PyObject *extend_fun(PyObject *self, PyObject *args);`\n",
    "  - 编写模块配置信息\n",
    "      - `static struct PyModuleDef module_name = {...}`\n",
    "      - 模块函数列表\n",
    "        - `static PyMethodDef methods[] = {{},...}`\n",
    "  - 编写模块初始化函数\n",
    "      - `PyMODINIT_FUNC PyInit_testlib(void){return PyModule_Create(&module_name);}`\n",
    "  - 构建（安装）扩展\n",
    "      - `setup.py`\n",
    "      - `python setup.py build_ext --inplace -f`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.5.2 扩展函数\n",
    "\n",
    "#### 扩展函数的定义\n",
    "\n",
    "- 参数与返回值\n",
    "  - 扩展函数就是一个接受一个Python对象元组作为参数，并返回一个新Python对象的C函数\n",
    "    - `static PyObject *extend_fun(PyObject *self, PyObject *args);`\n",
    "  - `PyObject`表示任何Python对象的C数据类型\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 参数解析\n",
    "  - `PyArg_ParseTuple`函数用于将Python中的值转换成C中对应表示类型，其参数包括格式字符串和用于存储传入数据的C变量的地址\n",
    "  - 格式字符串的作用是对传入的Python类型进行描述，比如`i`代表整数，`d`代表双精度浮点数\n",
    "  \n",
    "|格式字符|Python类型|C类型|含义|\n",
    "|:---|:---|:---|:---|\n",
    "|`s`|`str`|`const char *`|将一个 Unicode 对象转换成一个指向字符串的 C 指针|\n",
    "|`s*`|`str`或`bytes`|`Py_buffer`|既接受 Unicode 对象也接受类字节类型对象|\n",
    "|`s#`|`str`或只读`bytes`|`const char *, int 或 Py_ssize_t`|结果存储在两个 C 变量中，第一个是指向 C 字符串的指针，第二个是它的长度|\n",
    "|`z`|`str`或`None`|`const char *`|ython 对象为 `None`时C 指针设置为 `NULL`|\n",
    "|`U`|`str`|`PyObject *`|Python 对象是一个 Unicode 对象，且不进行转换|\n",
    "|`S`|`bytes`|`PyBytesObject *`|要求 Python 对象是一个 bytes 类型对象，且不进行转换|\n",
    "|`b`|`int`|`unsigned char`|将一个非负的 Python 整型转化成`unsigned char`|\n",
    "|`h`|`int`|`short int`|将一个 Python 整型转化成一个 C `short int` 短整型|\n",
    "|`i`|`int`|`int`|将一个 Python 整型转化成一个 C `int` 整型|\n",
    "|`l`|`int`|`long int`|将一个 Python 整型转化成一个 C `long int` 长整型|\n",
    "|`c`|`bytes`或`bytearray`|`char`|将一个__长度为 1__ 的 Python bytes 或者 bytearray 转化成 C `char` 字符类型|\n",
    "|`C`|`str`|`char`|将一个__长度为 1 __的Python ` str` 转化成一个 C` int` 整型类型|\n",
    "|`f`|`float`|`float`|将一个 Python 浮点数转化成一个 C `float` 浮点数|\n",
    "|`d`|`float`|`double`|将一个Python浮点数转化成一个C `double` 双精度浮点数|\n",
    "|`D`|`complex`|`Py_complex`|将一个 Python 复数类型转化成一个 C Py_complex Python 复数类型|\n",
    "|`O`|`object`|`PyObject *`|将 Python 对象（不进行转换）存储在 C 对象指针中|\n",
    "|`O!`|`object`|`typeobject, PyObject *`|和 `O` 类似，但是需要两个 C 参数：第一个是 Python 类型对象的地址，第二个是存储对象指针的 C 变量的地址|\n",
    "|`O&`|`object`|`converter, anything`|通过一个 `converter*`函数将一个 Python 对象转换 C 变量。需要两个参数：第一个是函数，第二个是 C 变量的地址(任意类型的)，转化为 `void *` 类型|\n",
    "|`p`|`bool`|`int`|结果Python布尔类型转化为 C  整型值1或0。|"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "```C\n",
    "// (1) Python参数列表包含两个整数\n",
    "int x, y;\n",
    "PyArg_ParseTuple(args, \"ii\", &x, &y);\n",
    "\n",
    "// (2) Python参数列表包含三个参数，分别为字符串、整数和浮点数\n",
    "const char* x;\n",
    "int y;\n",
    "double z;\n",
    "PyArg_ParseTuple(args, \"sid\", &x, &y, &z)\n",
    "\n",
    "// (2) Python参数为列表\n",
    "PyObject *seq;\n",
    "PyArg_ParseTuple(args, \"O\", &seq);\n",
    "seq = PySequence_List(seq);\n",
    "int seqlen = PySequence_Length(seq);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 构造返回值\n",
    "\n",
    "- `Py_BuildValue`函数根据C数据类型创建Python对象，它也接受一个格式字符串来指定期望类型，其过程与`PyArg_ParseTuple`相反\n",
    "  \n",
    "| 格式                                 | 对应Python值               |\n",
    "| :----------------------------------- | :------------------------- |\n",
    "| `Py_BuildValue(\"\")`                   | `None`                       |\n",
    "| `Py_BuildValue(\"i\", 123)`              | `123`                        |\n",
    "| `Py_BuildValue(\"iii\", 123, 456, 789)`   | `(123, 456, 789)`            |\n",
    "| `Py_BuildValue(\"s\", \"hello\")`           | `'hello'`                    |\n",
    "| `Py_BuildValue(\"y\", \"hello\")`           | `b'hello'`                   |\n",
    "| `Py_BuildValue(\"ss\", \"hello\", \"world\")` | `('hello', 'world')`         |\n",
    "| `Py_BuildValue(\"s#\", \"hello\", 4)`       | `'hell'`                     |\n",
    "| `Py_BuildValue(\"y#\", \"hello\", 4)`       | `b'hell'`                    |\n",
    "| `Py_BuildValue(\"()\")`                   | `()`                         |\n",
    "| `Py_BuildValue(\"(i)\", 123)`             | `(123,)`                     |\n",
    "| `Py_BuildValue(\"(ii)\", 123, 456)`       | `(123, 456)`                 |\n",
    "| `Py_BuildValue(\"(i,i)\", 123, 456)`      | `(123, 456)`                 |\n",
    "| `Py_BuildValue(\"[i,i]\", 123, 456)`     | `[123, 456]`                 |\n",
    "| `Py_BuildValue(\"{s:i,s:i}\", \"abc\", 123, \"def\", 456)` | `{'abc': 123, 'def': 456}`   |\n",
    "| `Py_BuildValue(\"((ii)(ii)) (ii))\", 1, 2, 3, 4, 5, 6)` | `(((1, 2), (3, 4)), (5, 6))` |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.5.3 模块配置与初始化\n",
    "\n",
    "- 模块的配置信息是一个`PyModuleDef`类型的特殊结构体，重要的成员有\n",
    "  - `m_base`，用于初始化模块的公共信息，取值通常为`PyModuleDef_HEAD_INIT`\n",
    "  - `m_name`，模块名称;\n",
    "  - `m_doc`，模块的文档字符串;\n",
    "  - `m_size`，解释器状态大小，`-1` 表示用全局变量保存状态;\n",
    "  - `m_methods`，模块函数列表，为一个`PyMethodsDef`类型的结构体数组\n",
    "    - `ml_name`，Python 可见的扩展模块中的函数名;\n",
    "    - `ml_meth`，扩展函数;\n",
    "    - `ml_flags`，调用扩展函数时的参数传递方式，取值常为`METH_VARARGS`，表示扩展函数传入`self`和`args`两个参数(参见第9.5.2小节第1部分);\n",
    "    - `ml_doc`，函数的文档字符串。\n",
    "  \n",
    "- 定义模块初始化函数\n",
    "  - Python 中使用`import`语句导入模块时被调用\n",
    "  - 必须命名为`PyInit_XXX`，其中`XXX`为模块名"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.5.4 扩展的构建与安装\n",
    "\n",
    "- 编写setup.py文件\n",
    "- 在命令行中使用`python setup.py install`命令编译扩展模块并安装至 Python 环境之中\n",
    "- 也可以使用`python setup.py build_ext --inplace`仅生成可被 Python 直接调用的动态链接库文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.5.5 示例\n",
    "\n",
    "- C函数库\n",
    "\n",
    "```C\n",
    "// testlib.c\n",
    "#include <math.h>\n",
    "\n",
    "// 最大公约数\n",
    "int gcd(int x, int y) {\n",
    "    int g = y;\n",
    "    while (x > 0) {\n",
    "        g = x;\n",
    "        x = y % x;\n",
    "        y = g;\n",
    "    }\n",
    "    return g;\n",
    "}\n",
    "\n",
    "// 除法\n",
    "int divide(int a, int b, int *remainder) {\n",
    "    int quot = a / b;\n",
    "    *remainder = a % b;\n",
    "    return quot;\n",
    "}\n",
    "\n",
    "// 列表求均值\n",
    "double avg(double *a, int n) {\n",
    "    int i;\n",
    "    double total = 0.0;\n",
    "    for (i = 0; i < n; i++) {\n",
    "        total += a[i];\n",
    "    }\n",
    "    return total / n;\n",
    "}\n",
    "\n",
    "// 结构体\n",
    "typedef struct Point {\n",
    "    double x,y;\n",
    "} Point;\n",
    "\n",
    "// 结构体参数\n",
    "double distance(Point *p1, Point *p2) {\n",
    "    return hypot(p1->x - p2->x, p1->y - p2->y);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 编译\n",
    "\n",
    "```\n",
    "gcc -o testlib.o -c testlib.c\n",
    "```\n",
    "\n",
    "- C函数库的头文件用于在扩展函数中调用C函数库\n",
    "\n",
    "```C\n",
    "// testlib.h\n",
    "\n",
    "#include <math.h>\n",
    "\n",
    "extern int gcd(int, int);\n",
    "extern int divide(int a, int b, int *remainder);\n",
    "extern double avg(double *a, int n);\n",
    "\n",
    "typedef struct Point {\n",
    "    double x,y;\n",
    "} Point;\n",
    "\n",
    "extern double distance(Point *p1, Point *p2);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 编写扩展模块\n",
    "\n",
    "```C\n",
    "\n",
    "// testlibpy.c\n",
    "\n",
    "#include \"Python.h\"\n",
    "#include \"testlib.h\"\n",
    "\n",
    "// 函数\n",
    "static PyObject *py_gcd(PyObject *self, PyObject *args)\n",
    "{\n",
    "    int x, y, result;\n",
    "\n",
    "    if (!PyArg_ParseTuple(args, \"ii\", &x, &y))\n",
    "    {\n",
    "        return NULL;\n",
    "    }\n",
    "    result = gcd(x, y);\n",
    "    return Py_BuildValue(\"i\", result);\n",
    "}\n",
    "\n",
    "// 指针作为参数\n",
    "static PyObject *py_divide(PyObject *self, PyObject *args)\n",
    "{\n",
    "    int a, b, quotient, remainder;\n",
    "    if (!PyArg_ParseTuple(args, \"ii\", &a, &b))\n",
    "    {\n",
    "        return NULL;\n",
    "    }\n",
    "    quotient = divide(a, b, &remainder);\n",
    "    return Py_BuildValue(\"(ii)\", quotient, remainder);\n",
    "}\n",
    "\n",
    "// 列表作为参数\n",
    "static PyObject *py_avg(PyObject *self, PyObject *args)\n",
    "{\n",
    "    PyObject *seq;\n",
    "    double *dbar, result;\n",
    "    int seqlen;\n",
    "\n",
    "    // 获取序列参数\n",
    "    if (!PyArg_ParseTuple(args, \"O\", &seq))\n",
    "        return 0;\n",
    "    seq = PySequence_List(seq);\n",
    "\n",
    "    // 将序列复制为double数组\n",
    "    seqlen = PySequence_Length(seq);\n",
    "    dbar = malloc(seqlen * sizeof(double));\n",
    "    for (int i = 0; i < seqlen; i++)\n",
    "    {\n",
    "        PyObject *item = PyList_GetItem(seq, i);\n",
    "        dbar[i] = PyFloat_AsDouble(PyNumber_Float(item));\n",
    "    }\n",
    "\n",
    "    // 释放空间，计算并返回结果\n",
    "    Py_DECREF(seq);\n",
    "    result = avg(dbar, seqlen);\n",
    "    free(dbar);\n",
    "    return Py_BuildValue(\"d\", result);\n",
    "}\n",
    "\n",
    "// Point 对象的 Destructor 函数\n",
    "static void del_Point(PyObject *obj)\n",
    "{\n",
    "    free(PyCapsule_GetPointer(obj, \"Point\"));\n",
    "}\n",
    "\n",
    "// 结构体：创建 Point 对象\n",
    "static PyObject *py_Point(PyObject *self, PyObject *args)\n",
    "{\n",
    "    Point *p;\n",
    "    double x, y;\n",
    "    if (!PyArg_ParseTuple(args, \"dd\", &x, &y))\n",
    "    {\n",
    "        return NULL;\n",
    "    }\n",
    "    p = (Point *)malloc(sizeof(Point));\n",
    "    p->x = x;\n",
    "    p->y = y;\n",
    "\n",
    "    // 创建capsule。capsule类似于指针\n",
    "    return PyCapsule_New(p, \"Point\", del_Point);\n",
    "}\n",
    "\n",
    "// 结构体作为参数\n",
    "static PyObject *py_distance(PyObject *self, PyObject *args)\n",
    "{\n",
    "    Point *p1, *p2;\n",
    "    PyObject *py_p1, *py_p2;\n",
    "    double result;\n",
    "\n",
    "    if (!PyArg_ParseTuple(args, \"OO\", &py_p1, &py_p2))\n",
    "    {\n",
    "        return NULL;\n",
    "    }\n",
    "    \n",
    "    // 提取capsule中的指针\n",
    "    p1 = (Point *)PyCapsule_GetPointer(py_p1, \"Point\");\n",
    "    p2 = (Point *)PyCapsule_GetPointer(py_p2, \"Point\");\n",
    "\n",
    "    result = distance(p1, p2);\n",
    "    return Py_BuildValue(\"d\", result);\n",
    "}\n",
    "\n",
    "// 模块函数列表\n",
    "static PyMethodDef methods[] = {\n",
    "    {\"gcd\", py_gcd, METH_VARARGS, \"greatest common divisor\"},\n",
    "    {\"divide\", py_divide, METH_VARARGS, \"integer division\"},\n",
    "    {\"avg\", py_avg, METH_VARARGS, \"list average\"},\n",
    "    {\"distance\", py_distance, METH_VARARGS, \"point distance\"},\n",
    "    {\"Point\", py_Point, METH_VARARGS, \"point\"},\n",
    "    {NULL, NULL, 0, NULL}};\n",
    "\n",
    "// 配置模块\n",
    "static struct PyModuleDef module = {\n",
    "    PyModuleDef_HEAD_INIT,\n",
    "    \"testlib\",        // 模块名\n",
    "    \"A lib for test\", // 模块文档字符串\n",
    "    -1,               // 解释器状态大小，-1表示用全局变量保存状态\n",
    "    methods};\n",
    "\n",
    "// 模块初始化函数\n",
    "PyMODINIT_FUNC PyInit_testlib(void)\n",
    "{\n",
    "    return PyModule_Create(&module);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- setup文件\n",
    "\n",
    "```python\n",
    "# setup.py\n",
    "from distutils.core import setup, Extension\n",
    "\n",
    "setup(name='testlib',\n",
    "      ext_modules=[\n",
    "          Extension('testlib',\n",
    "                    ['testlibpy.c'],\n",
    "                    include_dirs=['/path/to/python/env/include/python3.8'],\n",
    "                    library_dirs=['/path/to/file/testlib.o/'],\n",
    "                    libraries=['testlib.o']\n",
    "                    )\n",
    "      ]\n",
    "      )\n",
    "```\n",
    "\n",
    "- 扩展模块构建\n",
    "\n",
    "```\n",
    "python setup.py build_ext --inplace\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 调用\n",
    "\n",
    "```\n",
    ">>> from testlib import *\n",
    ">>> gcd(35,42)\n",
    "7\n",
    ">>> divide(42, 8)\n",
    "(5, 2)\n",
    ">>> avg([1, 2, 3, 4, 5, 6])\n",
    "3.5\n",
    ">>> p1 = Point(1, 2)\n",
    ">>> p2 = Point(4, 5)\n",
    ">>> distance(p1, p2)\n",
    "4.242640687119285\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 9.6 项目打包与发布\n",
    "\n",
    "- PyPI\n",
    "  - Python 第三方工具包的官方仓库\n",
    "  - 使用pip安装的第三方工具包就来自 PyPI"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.6.1 打包与发布的流程\n",
    "\n",
    "- 打包与发布的流程\n",
    "  - 注册 PyPI 帐户:在 PyPI 官方注册帐户信息\n",
    "  - 项目配置:编写与配置setup.py和其他相关文件\n",
    "  - 打包:执行setup.py文件，将项目打包为所需要的格式\n",
    "  - 发布:利用twine上传工具包。首次上传会自动创建项目，并且要求与 PyPI 仓库中已有的工具包不重复;以后每次上传要求版本号不重复"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 注册 PyPI 帐户\n",
    "\n",
    "<center>\n",
    "    <img src=\"./figures/fig9-2.png\" width=\"65%\">\n",
    "</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 项目配置\n",
    "  - setup 常用配置参数\n",
    "\n",
    "| 参数                            | 功能                                           |\n",
    "| :----------------------------- | :------------------------------ |\n",
    "| `description`                   | 工具包简要描述                                 |\n",
    "| `long_description`              | 工具包详细描述，常读取自 Markdown 文件         |\n",
    "| `long_description_content_type` | 详细描述的格式，常取值为 text/markdown         |\n",
    "| `classifiers`                   | 项目的分类标识，作为 PyPI 对项目进行分类的依据 |\n",
    "| `keywords`                      | 项目关键字列表                                 |\n",
    "| `packages`                      | 项目所中包的列表(包含 `__init__.py` 的文件夹)  |\n",
    "| `py_modules`                    | 包之外的独立模块文件名                         |\n",
    "| `package_data`                  | 工具包所需的数据文件                           |\n",
    "| `data_files`                    | 需要打包的数据文件，如图片，配置文件等         |\n",
    "| `ext_modules`                   | 扩展模块配置信息                               |\n",
    "| `install_requires`              | 该工具包所依赖的其他工具包列表                 |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 打包\n",
    "  - 执行setup.py\n",
    "  - 常用子命令\n",
    "    - `install`:打包并将build目录中的文件安装至当前 Python 环境;\n",
    "    - `build`:构建生成安装工具包所需的所有文件\n",
    "    - `clean`:清除打包过程中生成的临时文件\n",
    "    - `check`:检测配置信息是否有误\n",
    "    - `build_py`:构建纯 Python 模块\n",
    "    - `build_ext`:构建 C/C++ 扩展模块\n",
    "    - `sdist`:创建源代码发布文件\n",
    "    - `bdist`:创建二进制发布文件\n",
    "    - `bdist_wheel`:创建 wheel 格式的发布文件;\n",
    "    - `bdist_egg`:创建 egg 格式的发布文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 发布\n",
    "  - `python -m twine upload dist/*`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 9.6.2 项目打包与发布示例\n",
    "\n",
    "- 项目的文件目录结构\n",
    "\n",
    "<center>\n",
    "    <img src=\"./figures/fig9-3.png\" width=\"20%\"/>\n",
    "</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "```python\n",
    "# 文件 testlibpy/functions.py\n",
    "import numpy as np\n",
    "\n",
    "def add(x, y):\n",
    "    return x + y\n",
    "\n",
    "def average(lst):\n",
    "    return np.average(lst)\n",
    "\n",
    "class Point:\n",
    "    def __init__(self, x, y):\n",
    "        self.x, self.y = x, y\n",
    "\n",
    "def distance(p1, p2):\n",
    "    return np.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "# 文件 testlibpy/__init__.py\n",
    "from .functions import *\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "# 文件 setup.py\n",
    "from setuptools import setup, find_packages\n",
    "\n",
    "desc = '工具包的简要说明'\n",
    "long_description = open('./README.md').read()\n",
    "\n",
    "setup(\n",
    "    name=\"testlibpy\",\n",
    "    version=\"0.0.1\",\n",
    "    author=\"pystudy\",\n",
    "    author_email=\"xxxxxx@xxxxxx.com\",\n",
    "    license='MIT',\n",
    "    description=desc,\n",
    "    long_description=long_description,\n",
    "    long_description_content_type='text/markdown',\n",
    "    url=\"https://xxxxxx.com/testlibpy\",\n",
    "    classifiers=[ 'Development Status :: 3 - Alpha',\n",
    "                  'Programming Language :: Python :: 3',\n",
    "                  'Operating System :: OS Independent'],\n",
    "    packages=find_packages(include=['testlibpy']), \n",
    "    install_requires=['numpy > 1.15'],\n",
    "    python_requires='>=3.6',\n",
    ")\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 打包：`$ python setup.py sdist bdist_wheel`\n",
    "- 发布：`$ python -m twine upload dist/*`"
   ]
  }
 ],
 "metadata": {
  "author": "liuchen",
  "celltoolbar": "幻灯片",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.1"
  },
  "rise": {
   "enable_chalkboard": true,
   "footer": "",
   "progress": true,
   "scroll": true,
   "slideNumber": true
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "url": "https://github.com/hitlic/python_book"
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
